package jdx_14_基于CAS的新锁;

import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/*
如果在读取数据的时候不加锁，那么写入线程很有可能写到一半数据被读取了(脏读)
 */
public class T10_TestReadWriteLock {
    static Lock lock = new ReentrantLock();
    private static int value;

    static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static Lock readLock = readWriteLock.readLock();  //读锁：共享锁
    static Lock writeLock = readWriteLock.writeLock();//写锁：排他锁

    public static void read(Lock lock) {
        try {
            lock.lock();
            Thread.sleep(1000);
            System.out.println("read over!");
            //模拟读取操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void write(Lock lock, int v) {
        try {
            lock.lock();
            Thread.sleep(1000);
            value = v;
            System.out.println("write over!");
            //模拟写操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }





    public static void main(String[] args) {
        /*
        [实验一]:使用 排他锁
        由于每条线程每次都是1秒执行1次，所以最终执行整个流程，至少需要 20 秒。

        [实验二]:使用 读写锁
        由于读锁可以同时进行读取，所以前面18条线程可以在很快的时间内读取完毕
        整个流程差不多2秒就能结束。

         */

//        Runnable readR = ()-> read(lock); //互斥锁 也叫 排他锁
        Runnable readR = ()-> read(readLock);
//        Runnable writeR = ()->write(lock, new Random().nextInt());
        Runnable writeR = ()->write(writeLock, new Random().nextInt());

        for(int i=0; i<18; i++) new Thread(readR).start();
        for(int i=0; i<2; i++) new Thread(writeR).start();
    }
}


/*
有些业务场景需要保证数据的一致，当有多条线程同时来进行擦操作的时候(既有读操作也有写操作)
如何保证读取的数据都是一致的呢？

[读]:当有多条线程同时需要读取的时候，如果每条线程都进行加锁处理，那么效率会非常低，
    于是我们可以在加一把特殊的锁，只要是读线程，都可以拿到这把锁。

[写]:写线程，二话不说，直接把整个操作锁死，期间不会让读线程进来执行，而是写完之后，
    读线程才能来读取。

 */
